/**
 * vue2 响应式原理
 * 1  对象不存在的属性 无法直接监听
 * 2 数组长度直接改变 无法监听
 * 3 默认会递归
 * */

/**
 * 前置知识：
 * 1 Object.defineProperty() 方法会直接在一个对象上定义一个新属性，或者修改一个对象的现有属性，并返回此对象。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
 * 重写数组相关：
 * 1 Object.create() 方法用于创建一个新对象，使用现有的对象来作为新创建对象的原型（prototype） https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
 * 2 Function.prototype.call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
 * 3 Object.setPrototypeOf() 方法设置一个指定的对象的原型 ( 即，内部 [[Prototype]] 属性） https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
 *
 */


// 获取原生数组Array的原型
const oldArrayPrototype = Array.prototype
// 创建一个新对象 继承原生数组的原型
const proto = Object.create(oldArrayPrototype)
const arrayMethods = 'push splice shift unshift pop reverse'.split(' ')
for (const method of arrayMethods) {
    // 重写数组的方法
    proto[method] = function () {
        updateView()
        // 原生数组先调用一下
        oldArrayPrototype[method].call(this, ...arguments)
    }
}

/**
 * 观察
 * @param {*} target 需要观察的任意对象
 * @return {*}
 */
function observe(target) {
    if (typeof target !== 'object' || target === null) {
        // 如果 不是对象或者为null 直接返回
        return target
    }
    if (Array.isArray(target)) {
        // 拦截数组 将原型链指向重写后的 proto
        Object.setPrototypeOf(target, proto)
        // target.__proto__ = proto // 已废弃: 该特性已经从 Web 标准中删除
    }
    for (const key in target) {
        // 遍历每一个属性 实现响应式
        reactive(target, key, target[key])
    }
}

/**
 * 重写Object 里面的get set方法
 * @param target
 * @param key
 * @param value
 * @return {*}
 */
function reactive(target, key, value) {
    // 如果 value值本身就是一个对象 也要递归重写方法 才能实现响应
    observe(value)
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newVal) {
            if (newVal !== value) {
                // 如果新添加的值 也是一个对象也要递归重写 才能实现响应
                observe(newVal)
                // 如果新值不等于老值 替换老值
                value = newVal
                updateView()
            }
        }
    })
}

/**
 * 视图更新
 */
function updateView() {
    console.log('视图更新')
}

const obj = {
    name: '曾小晖',
    interest: {
        photography: '摄影',
        running: '跑步',
        badminton: '羽毛球',
    },
    love: [1, 2, 3]
}

observe(obj)
/*obj.name = '真帅'
console.log(obj.name)

// 多层对象
obj.interest.photography = '小清新摄影'
console.log(obj.interest.photography)

// 直接赋值一个新对象
obj.interest = {work: 'web前端'}
console.log(obj.interest.work)

// 直接修改 新添加的属性的值
obj.interest.work = 'web前端工程师'
console.log(obj.interest.work)*/

// 处理数组 push splice shift unshift pop reverse
// obj.love.push(4)
// console.log(obj.love)

// 属性不存在 不会观察到
obj.a = 12
console.log(obj.a)
